package server.connector.http;

import server.connector.ResponseStream;
import server.connector.ResponseWriter;
import server.util.RequestUtil;
import server.util.ResponseUtil;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Locale;

public class HttpResponseImpl extends HttpResponseBase implements HttpServletResponse {
    //后续实现---------------------------------------------------------------
    @Override
    public void setLocale(Locale loc) {

    }

    //----------------------------------------------------------------------
    //是否要关闭连接
    public boolean isCloseConnection() {
        String connection = getHeader("Connection");
        return connection != null && "close".equals(connection);
    }

    //删除响应头name中的value值
    public void removeHeader(String name, String value) {
        if (isCommitted())
            return;
        if (included)
            return;
        synchronized (headers) {
            ArrayList<String> values = headers.get(name);
            if (values != null && values.size() > 0) {
                values.remove(value);
                if (values.isEmpty())
                    headers.remove(name);
            }
        }
    }


    //实现接口的方法------------------------------------------------------------------------
    //响应行和响应头是否已经发送?
    @Override
    public boolean isCommitted() {
        //响应是否已经提交?
        return committed;
    }

    //添加Cookie
    @Override
    public void addCookie(Cookie cookie) {
        if (isCommitted())
            return;
        if (included)
            return;
        synchronized (cookies) {
            cookies.add(cookie);
        }
    }

    //是否存在名为name的响应头
    @Override
    public boolean containsHeader(String name) {
        synchronized (headers) {
            return headers.get(name) != null;
        }
    }

    //设置value是时间型的响应头
    @Override
    public void setDateHeader(String name, long date) {
        setHeader(name, format.format(new Date(date)));
    }

    //添加value是时间型的响应头
    @Override
    public void addDateHeader(String name, long date) {
        addHeader(name, format.format(new Date(date)));
    }

    //设置响应头
    //若headers中name键存在，会用旧值替换新值
    //若设置的是content-length或content-type属性，还会修改这些变量
    //若content-type包含字符编码,会修改字符编码
    @Override
    public void setHeader(String name, String value) {
        if (isCommitted())
            return;
        if (included)
            return;
        //添加请求头
        ArrayList<String> values = new ArrayList<>();
        values.add(value);
        synchronized (headers) {
            headers.put(name, values);
        }
        //处理特殊请求头
        String match = name.toLowerCase();
        if ("content-length".equals(match)) {
            int contentLength = -1;
            try {
                contentLength = Integer.parseInt(value);
            } catch (Exception e) {
            }
            if (contentLength >= 0) {
                setContentLength(contentLength);
            }
        } else if ("content-type".equals(match)) {
            setContentType(value);
        }

    }


    //添加响应头
    //若headers中name键存在，会将新值添加上去(新旧值同时存在)
    @Override
    public void addHeader(String name, String value) {
        if (isCommitted())
            return;
        if (included)
            return;
        synchronized (headers) {
            ArrayList<String> values = headers.get(name);
            if (values == null)
                values = new ArrayList<>();
            values.add(value);
            headers.put(name, values);
        }
    }

    //设置int型响应头
    @Override
    public void setIntHeader(String name, int value) {
        setHeader(name, "" + value);
    }

    //添加int型的响应头
    @Override
    public void addIntHeader(String name, int value) {
        addHeader(name, "" + value);
    }

    //返回name对应的响应头值,没有返回null
    @Override
    public String getHeader(String name) {
        synchronized (headers) {
            ArrayList<String> values = headers.get(name);
            if (values == null || values.size() <= 0)
                return null;
            return values.get(0);
        }
    }

    //返回name对应的响应头值集合,没有返回空集合
    @Override
    public Collection<String> getHeaders(String name) {
        ArrayList<String> values = null;
        synchronized (headers) {
            values = headers.get(name);
        }
        if (values == null || values.size() <= 0)
            return new ArrayList<>();
        return new ArrayList<>(values);
    }

    //返回请求头名称集合
    @Override
    public Collection<String> getHeaderNames() {
        synchronized (headers) {
            return new ArrayList<>(headers.keySet());
        }
    }

    //返回响应的字符集编码
    @Override
    public String getCharacterEncoding() {
        if (encoding == null)
            return Constants.DEFAULT_RESPONSE_ENCODING;
        return encoding;
    }

    //返回MINE类型
    @Override
    public String getContentType() {
        return this.contentType;
    }

    //设置内容媒体类型信息,格式:type/subtype ;parameter
    //如Content-Type: application/json;charset:utf-8;
    //如果设置中包含字符编码,会重新设置字符编码
    //否则会添加上字符编码
    @Override
    public void setContentType(String type) {
        if (isCommitted())
            return;
        if (included)
            return;
        this.contentType = type;
        if (type.contains(";")) {
            //解析出设置的字符集编码
            encoding = RequestUtil.parseCharacterEncoding(type);
            if (encoding == null)
                encoding = Constants.DEFAULT_RESPONSE_ENCODING;
        } else {
            //加上当前的字符集编码
            if (encoding != null)
                contentType = type + ";charset=" + encoding;
        }
    }

    //设置响应字符集
    //会改变Content-Type的charset
    @Override
    public void setCharacterEncoding(String charset) {
        byte[] test = new byte[1];
        test[0] = 'a';
        try {
            new String(test, charset);
        } catch (UnsupportedEncodingException e) {
            //不支持该字符集
            return;
        }
        //设置字符集
        encoding = charset;
        if (isCommitted())
            return;
        if (included)
            return;
        if (contentType != null) {
            int index = contentType.indexOf(';');
            if (index >= 0) {
                contentType = contentType.substring(0, index);
            }
            contentType = contentType + ";charset=" + encoding;
        } else {
            contentType = "text/html;charset=" + encoding;
        }
    }

    //设置内容长度
    @Override
    public void setContentLength(int len) {
        if (isCommitted())
            return;
        if (included)
            return;
        contentLength = len;
        if (responseStream != null)
            responseStream.checkChunking(this);
    }

    //设置内容长度(长度为long型)
    @Override
    public void setContentLengthLong(long len) {
        setContentLength((int) len);
    }

    //返回支持的语言所在地区
    @Override
    public Locale getLocale() {
        return locale;
    }

    //获取输出流
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        if (writer != null)
            throw new IllegalStateException("getWriter()已经被调用过");
        if (stream == null)
            stream = createOutputStream();
        ((ResponseStream) stream).setCommit(true);
        return stream;
    }

    //返回Writer
    @Override
    public PrintWriter getWriter() throws IOException {
        if (writer != null)
            return writer;
        if (stream != null)
            throw new IllegalStateException("getOutputStream()已经被调用过");
        ResponseStream newStream = (ResponseStream) createOutputStream();
        newStream.setCommit(false);
        OutputStreamWriter osw = new OutputStreamWriter(newStream, getCharacterEncoding());
        writer = new ResponseWriter(osw, newStream);
        stream = newStream;
        return writer;
    }

    //设置缓冲区大小
    @Override
    public void setBufferSize(int size) {
        if (isCommitted() || bufferCount > 0)
            throw new IllegalStateException("写入数据后无法更改缓冲区大小");
        if (buffer.length >= size)
            return;
        buffer = new byte[size];
    }

    //返回缓冲区大小
    @Override
    public int getBufferSize() {
        return buffer.length;
    }

    //把缓冲区存放的数据发送出去,若之前没有发送响应行和响应头,先发送行和头
    public void flushBuffer() throws IOException {
        //如果响应行和响应头没有被发送,则发送
        if (!isCommitted())
            sendLineAndHeaders();
        committed = true;
        if (bufferCount > 0) {
            try {
                output.write(buffer, 0, bufferCount);
            } finally {
                bufferCount = 0;
            }
        }
    }

    //复位缓冲区
    @Override
    public void resetBuffer() {
        if (committed)
            throw new IllegalStateException("响应已发送,无法复位");
        bufferCount = 0;
    }

    //复位响应
    @Override
    public void reset() {
        if (included)
            return;
        if (committed)
            throw new IllegalStateException("响应已经提交,无法复位");

        if (stream != null)
            ((ResponseStream) stream).reset();
        bufferCount = 0;
        contentLength = -1;
        contentType = null;

        String connectionValue = getHeader("Connection");
        String transferEncodingValue = getHeader("Transfer-Encoding");
        cookies.clear();
        headers.clear();
        message = null;
        status = HttpServletResponse.SC_OK;
        if (connectionValue != null)
            addHeader("Connection", connectionValue);
        if (transferEncodingValue != null)
            addHeader("Transfer-Encoding", transferEncodingValue);
    }

    //返回当前响应状态
    @Override
    public int getStatus() {
        return status;
    }

    //设置响应状态
    @Override
    public void setStatus(int status) {
        setStatus(status, ResponseUtil.getStatusMessage(status));

    }

    //设置响应状态和响应消息
    @Override
    public void setStatus(int status, String sm) {
        if (included)
            return;
        this.status = status;
        this.message = sm;
        if (responseStream != null)
            responseStream.checkChunking(this);
    }

    //如果url可以重写(加上sessionID),则重写返回,否则返回原来的url
    @Override
    public String encodeUrl(String url) {
        return encodeURL(url);
    }

    //如果url可以重写(加上sessionID),则重写返回,否则返回原来的url
    @Override
    public String encodeRedirectUrl(String url) {
        return encodeRedirectURL(url);
    }

    //如果url可以重写(加上sessionID),则重写返回,否则返回原来的url
    @Override
    public String encodeURL(String url) {
        if (isEncodeable(toAbsolute(url))) {
            return toEncoded(url, request.getSession().getId());
        } else {
            return url;
        }
    }

    //如果url可以重写(加上sessionID),则重写返回,否则返回原来的url
    @Override
    public String encodeRedirectURL(String url) {
        return encodeURL(url);
    }

    //重定向
    @Override
    public void sendRedirect(String location) throws IOException {
        if (isCommitted())
            throw new IllegalStateException("响应已经提交,无法重定向");
        if (included)
            return;
        //清除缓冲区的内容,不需要响应体
        resetBuffer();
        try {
            //转换成绝对地址
            String absolute = toAbsolute(location);
            setStatus(SC_MOVED_TEMPORARILY);
            setHeader("Location", absolute);
        } catch (IllegalArgumentException e) {
            setStatus(SC_NOT_FOUND);
        }
        setSuspended(true);
    }

    //发送错误响应
    @Override
    public void sendError(int sc, String msg) throws IOException {
        addHeader("Connection", "close");
        if (isCommitted())
            throw new IllegalStateException("响应已经提交");
        if (included)
            return;
        setError();
        status = sc;
        message = msg;
        resetBuffer();
        setSuspended(true);
    }

    //发送错误响应
    @Override
    public void sendError(int sc) throws IOException {
        sendError(sc, ResponseUtil.getStatusMessage(sc));
    }
}